#include "hal_soft_iic.h"

void    (*sda_set)(void) = NULL;
void    (*sda_reset)(void) = NULL;
uint8_t (*sda_get)(void) = NULL;
void    (*scl_set)(void) = NULL;
void    (*scl_reset)(void) = NULL;
void    (*sda_mode)(uint32_t mode) = NULL;

#define SDA_HIGH()  sda_set()
#define SDA_LOW()   sda_reset()
#define SDA_READ()  sda_get()
#define SCL_HIGH()  scl_set()
#define SCL_LOW()   scl_reset()
#define SDA_INPUT_MODE()  sda_mode(GPIO_Mode_IPU)
#define SDA_OUTPUT_MODE() sda_mode(GPIO_Mode_Out_PP)
#define _DELAY_US()  delay_us(2)

/**
 * @brief 初始化软件IIC引脚
 * 
 * 配置SDA和SCL引脚为开漏输出模式，并初始化为高电平。
 */
void hal_soft_iic_init(struct gpio_type *sda_gpio, struct gpio_type *scl_gpio)
{    
    sda_gpio->init(GPIO_Mode_Out_PP);
    scl_gpio->init(GPIO_Mode_Out_PP);

    sda_set = sda_gpio->set;
    sda_reset = sda_gpio->reset;
    sda_get = sda_gpio->read;
    scl_set = scl_gpio->set;
    scl_reset = scl_gpio->reset;    
    sda_mode = sda_gpio->init;
       
    SDA_HIGH();
    SCL_HIGH();
    _DELAY_US();
}

/**
 * @brief 产生IIC起始信号
 */
static void soft_iic_start(void)
{
    SDA_HIGH();
    SCL_HIGH();
    _DELAY_US();
    SDA_LOW();  // START条件：SCL高时SDA下降沿
    _DELAY_US();
    SCL_LOW();  // 钳住总线
}

/**
 * @brief 产生IIC停止信号
 */
static void soft_iic_stop(void)
{
    SCL_LOW();
    SDA_LOW();
    _DELAY_US();
    SCL_HIGH();
    _DELAY_US();
    SDA_HIGH();  // STOP条件：SCL高时SDA上升沿
}

uint8_t soft_iic_wait_Ack(void) {
    uint8_t timeout = 0;
    SDA_HIGH(); 
    SDA_INPUT_MODE();
    
    _DELAY_US();
    SCL_HIGH();
    _DELAY_US();
    
    while(SDA_READ()) {
        _DELAY_US();
        if(timeout++ > 250) {
            soft_iic_stop();
            return 1; // 超时无应答
        }
    }
    SCL_LOW();
    return 0;
}
// 发送ACK应答
void soft_iic_Ack(void) 
{
    SCL_LOW();
    SDA_OUTPUT_MODE();
    
    SDA_LOW();
    _DELAY_US();
    SCL_HIGH();
    _DELAY_US();
    SCL_LOW();
}
void soft_iic_nAck(void)
{
    SCL_LOW();
    SDA_OUTPUT_MODE();
    SDA_HIGH();
    _DELAY_US();
    SCL_HIGH();
    _DELAY_US();
    SCL_LOW();
}
/**
 * @brief 发送一个字节数据
 * 
 * @param data 要发送的数据
 * @return uint8_t 从设备的应答信号（0：应答，1：非应答）
 */
static uint8_t soft_iic_send_byte(uint8_t data)
{
    uint8_t i;
    SDA_OUTPUT_MODE();
    SCL_LOW();
    for(i=0; i<8; i++) {      
        if(data & 0x80) SDA_HIGH();
        else SDA_LOW();
        data <<= 1;
        _DELAY_US();
        SCL_HIGH();
        _DELAY_US();
        SCL_LOW();
        _DELAY_US();        
    }
}

/**
 * @brief 接收一个字节数据
 * 
 * @param ack 是否发送应答信号（0：应答，1：非应答）
 * @return uint8_t 接收到的数据
 */
static uint8_t soft_iic_receive_byte(uint8_t ack)
{
    uint8_t i, byte = 0;
    SDA_INPUT_MODE();
    
    for(i=0; i<8; i++) {
        SCL_LOW();
        _DELAY_US();
        SCL_HIGH();
        byte <<= 1;
        if(SDA_READ()) byte |= 0x01;
        _DELAY_US();
    }
    
    if(ack) 
        soft_iic_Ack();
    else 
        soft_iic_nAck();
    
    return byte;
}

/**
 * @brief 向指定设备写入数据
 * 
 * @param dev_addr 设备地址
 * @param reg_addr 寄存器地址
 * @param data 要写入的数据
 * @return uint8_t 操作结果（0：成功，1：失败）
 */
uint8_t hal_soft_iic_write(uint8_t dev_addr, uint8_t reg_addr, uint8_t data)
{
    soft_iic_start();
    soft_iic_send_byte(dev_addr); // 写模式地址
    if(soft_iic_wait_Ack())
    {
        return 0xFF; // 设备无应答
    }
    
    soft_iic_send_byte(reg_addr); // 发送寄存器地址
    soft_iic_wait_Ack();
    
    soft_iic_start(); // 重复起始条件
    soft_iic_send_byte(dev_addr | 0x01); // 读模式地址
    soft_iic_wait_Ack();
    
    data = soft_iic_receive_byte(0); // 读取数据(不发送ACK)
    soft_iic_stop();
    
    return data;
}
// 连续读取多个寄存器数据
void hal_soft_iic_Multi_write(uint8_t dev_addr, uint8_t start_reg, 
                            uint8_t *buf, uint16_t len) {
    soft_iic_start();
    soft_iic_send_byte(dev_addr); // 写模式地址
    if(soft_iic_wait_Ack())
    {
        return ; // 设备无应答
    }
    
    soft_iic_send_byte(start_reg); // 发送寄存器地址
    soft_iic_wait_Ack();
    
    soft_iic_start(); // 重复起始条件
    soft_iic_send_byte(dev_addr| 0x01); // 读模式地址
    soft_iic_wait_Ack();
    
    while(len--) {
        *buf++ = soft_iic_receive_byte(len ? 1 : 0); // 最后字节不发送ACK
    }
    soft_iic_stop();
}
/**
 * @brief 从指定设备读取数据
 * 
 * @param dev_addr 设备地址
 * @param reg_addr 寄存器地址
 * @param data 存储读取数据的指针
 * @return uint8_t 操作结果（0：成功，1：失败）
 */
uint8_t hal_soft_iic_read(uint8_t dev_addr, uint8_t reg_addr, uint8_t *data)
{
    soft_iic_start();
    soft_iic_send_byte(dev_addr); // 写模式地址
    if(soft_iic_wait_Ack()) {
        soft_iic_stop();
        return 1;
    }
        
    soft_iic_send_byte(reg_addr); // 寄存器地址
    if(soft_iic_wait_Ack()) return 2;
        
    soft_iic_start(); // 重复起始条件
    soft_iic_send_byte(dev_addr | 0x01); // 读模式
    if(soft_iic_wait_Ack()) return 3;
        
    *data = soft_iic_receive_byte(0); // 不发送ACK
    soft_iic_stop();

    return 0; // 成功
}
// 连续寄存器读取（自动地址递增）
uint8_t hal_soft_iic_Multi_read(uint8_t dev_addr, uint8_t start_reg, 
                               uint8_t *buf, uint16_t len) {
    soft_iic_start();
    soft_iic_send_byte(dev_addr); // 写模式地址
    if(soft_iic_wait_Ack()) {
        soft_iic_stop();
        return 1;
    }
    
    soft_iic_send_byte(start_reg); // 寄存器地址
    if(soft_iic_wait_Ack()) return 2;
    
    soft_iic_start(); // 重复起始条件
    soft_iic_send_byte(dev_addr | 0x01); // 读模式
    if(soft_iic_wait_Ack()) return 3;
    
    while(len--) {
        *buf++ = soft_iic_receive_byte(len ? 1 : 0); // 最后字节不发送ACK
    }
    soft_iic_stop();
    return 0;
}
